home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / TEXFONT / gentexfont.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  15.7 KB  |  561 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1997. */
  3.  
  4. /* This program is freely distributable without licensing fees  and is
  5.    provided without guarantee or warrantee expressed or  implied. This
  6.    program is -not- in the public domain. */
  7.  
  8. /* X compile line: cc -o gentexfont gentexfont.c -lX11 */
  9.  
  10. #include <assert.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <unistd.h>
  15. #include <X11/Xlib.h>
  16. #include <X11/Xutil.h>
  17. #include <math.h>
  18.  
  19. #include "TexFont.h"
  20.  
  21. typedef struct {
  22.   short width;
  23.   short height;
  24.   short xoffset;
  25.   short yoffset;
  26.   short advance;
  27.   unsigned char *bitmap;
  28. } PerGlyphInfo, *PerGlyphInfoPtr;
  29.  
  30. typedef struct {
  31.   int min_char;
  32.   int max_char;
  33.   int max_ascent;
  34.   int max_descent;
  35.   PerGlyphInfo glyph[1];
  36. } FontInfo, *FontInfoPtr;
  37.  
  38. Display *dpy;
  39. FontInfoPtr fontinfo;
  40. int format = TXF_FORMAT_BITMAP;
  41. int gap = 1;
  42.  
  43. /* #define REPORT_GLYPHS */
  44. #ifdef REPORT_GLYPHS
  45. #define DEBUG_GLYPH4(msg,a,b,c,d) printf(msg,a,b,c,d)
  46. #define DEBUG_GLYPH(msg) printf(msg)
  47. #else
  48. #define DEBUG_GLYPH4(msg,a,b,c,d) { /* nothing */ }
  49. #define DEBUG_GLYPH(msg) { /* nothing */ }
  50. #endif
  51.  
  52. #define MAX_GLYPHS_PER_GRAB 512  /* this is big enough for 2^9 glyph
  53.                                     character sets */
  54.  
  55. FontInfoPtr
  56. SuckGlyphsFromServer(Display * dpy, Font font)
  57. {
  58.   Pixmap offscreen;
  59.   XFontStruct *fontinfo;
  60.   XImage *image;
  61.   GC xgc;
  62.   XGCValues values;
  63.   int numchars;
  64.   int width, height, pixwidth;
  65.   int i, j;
  66.   XCharStruct *charinfo;
  67.   XChar2b character;
  68.   unsigned char *bitmapData;
  69.   int x, y;
  70.   int spanLength;
  71.   int charWidth, charHeight, maxSpanLength;
  72.   int grabList[MAX_GLYPHS_PER_GRAB];
  73.   int glyphsPerGrab = MAX_GLYPHS_PER_GRAB;
  74.   int numToGrab, thisglyph;
  75.   FontInfoPtr myfontinfo;
  76.  
  77.   fontinfo = XQueryFont(dpy, font);
  78.   if (!fontinfo)
  79.     return NULL;
  80.  
  81.   numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1;
  82.   if (numchars < 1)
  83.     return NULL;
  84.  
  85.   myfontinfo = (FontInfoPtr) malloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerGlyphInfo));
  86.   if (!myfontinfo)
  87.     return NULL;
  88.  
  89.   myfontinfo->min_char = fontinfo->min_char_or_byte2;
  90.   myfontinfo->max_char = fontinfo->max_char_or_byte2;
  91.   myfontinfo->max_ascent = fontinfo->max_bounds.ascent;
  92.   myfontinfo->max_descent = fontinfo->max_bounds.descent;
  93.  
  94.   width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing;
  95.   height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent;
  96.  
  97.   maxSpanLength = (width + 7) / 8;
  98.   /* Be careful determining the width of the pixmap; the X protocol allows
  99.      pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates
  100.      max out at 2^15-1 (signed short size).  If the width is too large, we
  101.      need to limit the glyphs per grab.  */
  102.   if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) {
  103.     glyphsPerGrab = (1 << 15) / (8 * maxSpanLength);
  104.   }
  105.   pixwidth = glyphsPerGrab * 8 * maxSpanLength;
  106.   offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)),
  107.     pixwidth, height, 1);
  108.  
  109.   values.font = font;
  110.   values.background = 0;
  111.   values.foreground = 0;
  112.   xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values);
  113.  
  114.   XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  115.   XSetForeground(dpy, xgc, 1);
  116.  
  117.   numToGrab = 0;
  118.   if (fontinfo->per_char == NULL) {
  119.     charinfo = &(fontinfo->min_bounds);
  120.     charWidth = charinfo->rbearing - charinfo->lbearing;
  121.     charHeight = charinfo->ascent + charinfo->descent;
  122.     spanLength = (charWidth + 7) / 8;
  123.   }
  124.   for (i = 0; i < numchars; i++) {
  125.     if (fontinfo->per_char != NULL) {
  126.       charinfo = &(fontinfo->per_char[i]);
  127.       charWidth = charinfo->rbearing - charinfo->lbearing;
  128.       charHeight = charinfo->ascent + charinfo->descent;
  129.       if (charWidth == 0 || charHeight == 0) {
  130.         /* Still must move raster pos even if empty character */
  131.         myfontinfo->glyph[i].width = 0;
  132.         myfontinfo->glyph[i].height = 0;
  133.         myfontinfo->glyph[i].xoffset = 0;
  134.         myfontinfo->glyph[i].yoffset = 0;
  135.         myfontinfo->glyph[i].advance = charinfo->width;
  136.         myfontinfo->glyph[i].bitmap = NULL;
  137.         goto PossiblyDoGrab;
  138.       }
  139.     }
  140.     grabList[numToGrab] = i;
  141.  
  142.     /* XXX is this right for large fonts? */
  143.     character.byte2 = (i + fontinfo->min_char_or_byte2) & 255;
  144.     character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8;
  145.  
  146.     /* XXX we could use XDrawImageString16 which would also paint the backing 
  147.  
  148.        rectangle but X server bugs in some scalable font rasterizers makes it 
  149.  
  150.        more effective to do XFillRectangles to clear the pixmap and
  151.        XDrawImage16 for the text.  */
  152.     XDrawString16(dpy, offscreen, xgc,
  153.       -charinfo->lbearing + 8 * maxSpanLength * numToGrab,
  154.       charinfo->ascent, &character, 1);
  155.  
  156.     numToGrab++;
  157.  
  158.   PossiblyDoGrab:
  159.  
  160.     if (numToGrab >= glyphsPerGrab || i == numchars - 1) {
  161.       image = XGetImage(dpy, offscreen,
  162.         0, 0, pixwidth, height, 1, XYPixmap);
  163.       for (j = 0; j < numToGrab; j++) {
  164.         thisglyph = grabList[j];
  165.         if (fontinfo->per_char != NULL) {
  166.           charinfo = &(fontinfo->per_char[thisglyph]);
  167.           charWidth = charinfo->rbearing - charinfo->lbearing;
  168.           charHeight = charinfo->ascent + charinfo->descent;
  169.           spanLength = (charWidth + 7) / 8;
  170.         }
  171.         bitmapData = calloc(height * spanLength, sizeof(char));
  172.         if (!bitmapData)
  173.           goto FreeFontAndReturn;
  174.         DEBUG_GLYPH4("index %d, glyph %d (%d by %d)\n",
  175.           j, thisglyph + fontinfo->min_char_or_byte2, charWidth, charHeight);
  176.         for (y = 0; y < charHeight; y++) {
  177.           for (x = 0; x < charWidth; x++) {
  178.             /* XXX The algorithm used to suck across the font ensures that
  179.                each glyph begins on a byte boundary.  In theory this would
  180.                make it convienent to copy the glyph into a byte oriented
  181.                bitmap.  We actually use the XGetPixel function to extract
  182.                each pixel from the image which is not that efficient.  We
  183.                could either do tighter packing in the pixmap or more
  184.                efficient extraction from the image.  Oh well.  */
  185.             if (XGetPixel(image, j * maxSpanLength * 8 + x, charHeight - 1 - y)) {
  186.               DEBUG_GLYPH("x");
  187.               bitmapData[y * spanLength + x / 8] |= (1 << (x & 7));
  188.             } else {
  189.               DEBUG_GLYPH(" ");
  190.             }
  191.           }
  192.           DEBUG_GLYPH("\n");
  193.         }
  194.         myfontinfo->glyph[thisglyph].width = charWidth;
  195.         myfontinfo->glyph[thisglyph].height = charHeight;
  196.         myfontinfo->glyph[thisglyph].xoffset = charinfo->lbearing;
  197.         myfontinfo->glyph[thisglyph].yoffset = -charinfo->descent;
  198.         myfontinfo->glyph[thisglyph].advance = charinfo->width;
  199.         myfontinfo->glyph[thisglyph].bitmap = bitmapData;
  200.       }
  201.       XDestroyImage(image);
  202.       numToGrab = 0;
  203.       /* do we need to clear the offscreen pixmap to get more? */
  204.       if (i < numchars - 1) {
  205.         XSetForeground(dpy, xgc, 0);
  206.         XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  207.         XSetForeground(dpy, xgc, 1);
  208.       }
  209.     }
  210.   }
  211.   XFreeGC(dpy, xgc);
  212.   XFreePixmap(dpy, offscreen);
  213.   return myfontinfo;
  214.  
  215. FreeFontAndReturn:
  216.   XDestroyImage(image);
  217.   XFreeGC(dpy, xgc);
  218.   XFreePixmap(dpy, offscreen);
  219.   for (j = i - 1; j >= 0; j--) {
  220.     if (myfontinfo->glyph[j].bitmap)
  221.       free(myfontinfo->glyph[j].bitmap);
  222.   }
  223.   free(myfontinfo);
  224.   return NULL;
  225. }
  226.  
  227. void
  228. printGlyph(FontInfoPtr font, int c)
  229. {
  230.   PerGlyphInfoPtr glyph;
  231.   unsigned char *bitmapData;
  232.   int width, height, spanLength;
  233.   int x, y;
  234.  
  235.   if (c < font->min_char || c > font->max_char) {
  236.     printf("out of range glyph\n");
  237.     return;
  238.   }
  239.   glyph = &font->glyph[c - font->min_char];
  240.   bitmapData = glyph->bitmap;
  241.   if (bitmapData) {
  242.     width = glyph->width;
  243.     spanLength = (width + 7) / 8;
  244.     height = glyph->height;
  245.  
  246.     for (y = 0; y < height; y++) {
  247.       for (x = 0; x < width; x++) {
  248.         if (bitmapData[y * spanLength + x / 8] & (1 << (x & 7))) {
  249.           putchar('X');
  250.         } else {
  251.           putchar('.');
  252.         }
  253.       }
  254.       putchar('\n');
  255.     }
  256.   }
  257. }
  258.  
  259. void
  260. getMetric(FontInfoPtr font, int c, TexGlyphInfo * tgi)
  261. {
  262.   PerGlyphInfoPtr glyph;
  263.   unsigned char *bitmapData;
  264.  
  265.   tgi->c = c;
  266.   if (c < font->min_char || c > font->max_char) {
  267.     tgi->width = 0;
  268.     tgi->height = 0;
  269.     tgi->xoffset = 0;
  270.     tgi->yoffset = 0;
  271.     tgi->dummy = 0;
  272.     tgi->advance = 0;
  273.     return;
  274.   }
  275.   glyph = &font->glyph[c - font->min_char];
  276.   bitmapData = glyph->bitmap;
  277.   if (bitmapData) {
  278.     tgi->width = glyph->width;
  279.     tgi->height = glyph->height;
  280.     tgi->xoffset = glyph->xoffset;
  281.     tgi->yoffset = glyph->yoffset;
  282.   } else {
  283.     tgi->width = 0;
  284.     tgi->height = 0;
  285.     tgi->xoffset = 0;
  286.     tgi->yoffset = 0;
  287.   }
  288.   tgi->dummy = 0;
  289.   tgi->advance = glyph->advance;
  290. }
  291.  
  292. int
  293. glyphCompare(const void *a, const void *b)
  294. {
  295.   unsigned char *c1 = (unsigned char *) a;
  296.   unsigned char *c2 = (unsigned char *) b;
  297.   TexGlyphInfo tgi1;
  298.   TexGlyphInfo tgi2;
  299.  
  300.   getMetric(fontinfo, *c1, &tgi1);
  301.   getMetric(fontinfo, *c2, &tgi2);
  302.   return tgi2.height - tgi1.height;
  303. }
  304.  
  305. int
  306. getFontel(unsigned char *bitmapData, int spanLength, int i, int j)
  307. {
  308.   return bitmapData[i * spanLength + j / 8] & (1 << (j & 7)) ? 255 : 0;
  309. }
  310.  
  311. void
  312. placeGlyph(FontInfoPtr font, int c, unsigned char *texarea, int stride, int x, int y)
  313. {
  314.   PerGlyphInfoPtr glyph;
  315.   unsigned char *bitmapData;
  316.   int width, height, spanLength;
  317.   int i, j;
  318.  
  319.   if (c < font->min_char || c > font->max_char) {
  320.     printf("out of range glyph\n");
  321.     return;
  322.   }
  323.   glyph = &font->glyph[c - font->min_char];
  324.   bitmapData = glyph->bitmap;
  325.   if (bitmapData) {
  326.     width = glyph->width;
  327.     spanLength = (width + 7) / 8;
  328.     height = glyph->height;
  329.  
  330.     for (i = 0; i < height; i++) {
  331.       for (j = 0; j < width; j++) {
  332.         texarea[stride * (y + i) + x + j] =
  333.           getFontel(bitmapData, spanLength, i, j);
  334.       }
  335.     }
  336.   }
  337. }
  338.  
  339. char *
  340. nodupstring(char *s)
  341. {
  342.   int len, i, p;
  343.   char *string;
  344.  
  345.   len = (int) strlen(s);
  346.   string = (char *) calloc(len + 1, 1);
  347.   p = 0;
  348.   for (i = 0; i < len; i++) {
  349.     if (!strchr(string, s[i])) {
  350.       string[p] = s[i];
  351.       p++;
  352.     }
  353.   }
  354.   string = realloc(string, p + 1);
  355.   return string;
  356. }
  357.  
  358. void
  359. main(int argc, char *argv[])
  360. {
  361.   int texw, texh;
  362.   unsigned char *texarea, *texbitmap;
  363.   FILE *file;
  364.   int len, stride;
  365.   unsigned char *glist;
  366.   int width, height;
  367.   int px, py, maxheight;
  368.   TexGlyphInfo tgi;
  369.   int usageError = 0;
  370.   char *fontname, *filename;
  371.   XFontStruct *xfont;
  372.   int endianness;
  373.   int i, j;
  374.  
  375.   texw = texh = 256;
  376.   glist = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijmklmnopqrstuvwxyz?.;,!*:\"/+@#$%^&()";
  377.   fontname = "-adobe-courier-bold-r-normal--46-*-100-100-m-*-iso8859-1";
  378.   filename = "default.txf";
  379.  
  380.   for (i = 1; i < argc; i++) {
  381.     if (!strcmp(argv[i], "-w")) {
  382.       i++;
  383.       texw = atoi(argv[i]);
  384.     } else if (!strcmp(argv[i], "-h")) {
  385.       i++;
  386.       texh = atoi(argv[i]);
  387.     } else if (!strcmp(argv[i], "-gap")) {
  388.       i++;
  389.       gap = atoi(argv[i]);
  390.     } else if (!strcmp(argv[i], "-byte")) {
  391.       format = TXF_FORMAT_BYTE;
  392.       break;
  393.     } else if (!strcmp(argv[i], "-bitmap")) {
  394.       format = TXF_FORMAT_BITMAP;
  395.     } else if (!strcmp(argv[i], "-glist")) {
  396.       i++;
  397.       glist = (unsigned char *) argv[i];
  398.     } else if (!strcmp(argv[i], "-fn")) {
  399.       i++;
  400.       fontname = argv[i];
  401.     } else if (!strcmp(argv[i], "-file")) {
  402.       i++;
  403.       filename = argv[i];
  404.     } else {
  405.       usageError = 1;
  406.     }
  407.   }
  408.  
  409.   if (usageError) {
  410.     putchar('\n');
  411.     printf("usage: texfontgen [options] txf-file\n");
  412.     printf(" -w #          textureWidth (def=%d)\n", texw);
  413.     printf(" -h #          textureHeight (def=%d)\n", texh);
  414.     printf(" -gap #        gap between glyphs (def=%d)\n", gap);
  415.     printf(" -bitmap       use a bitmap encoding (default)\n");
  416.     printf(" -byte         use a byte encoding (less compact)\n");
  417.     printf(" -glist ABC    glyph list (def=%s)\n", glist);
  418.     printf(" -fn name      X font name (def=%s)\n", fontname);
  419.     printf(" -file name    output file for textured font (def=%s)\n", fontname);
  420.     putchar('\n');
  421.     exit(1);
  422.   }
  423.   texarea = calloc(texw * texh, sizeof(unsigned char));
  424.   glist = (unsigned char *) nodupstring((char *) glist);
  425.  
  426.   dpy = XOpenDisplay(NULL);
  427.   if (!dpy) {
  428.     printf("could not open display\n");
  429.     exit(1);
  430.   }
  431.   /* find an OpenGL-capable RGB visual with depth buffer */
  432.   xfont = XLoadQueryFont(dpy, fontname);
  433.   if (!xfont) {
  434.     printf("could not get load X font: %s\n", fontname);
  435.     exit(1);
  436.   }
  437.   fontinfo = SuckGlyphsFromServer(dpy, xfont->fid);
  438.   if (!fontinfo) {
  439.     printf("could not get font glyphs\n");
  440.     exit(1);
  441.   }
  442.   len = (int) strlen((char *) glist);
  443.   qsort(glist, len, sizeof(unsigned char), glyphCompare);
  444.  
  445.   file = fopen(filename, "wb");
  446.   if (!file) {
  447.     printf("could not open %s for writing\n", filename);
  448.     exit(1);
  449.   }
  450.   fwrite("\377txf", 1, 4, file);
  451.   endianness = 0x12345678;
  452.   /*CONSTANTCONDITION*/
  453.   assert(sizeof(int) == 4);  /* Ensure external file format size. */
  454.   fwrite(&endianness, sizeof(int), 1, file);
  455.   fwrite(&format, sizeof(int), 1, file);
  456.   fwrite(&texw, sizeof(int), 1, file);
  457.   fwrite(&texh, sizeof(int), 1, file);
  458.   fwrite(&fontinfo->max_ascent, sizeof(int), 1, file);
  459.   fwrite(&fontinfo->max_descent, sizeof(int), 1, file);
  460.   fwrite(&len, sizeof(int), 1, file);
  461.  
  462.   px = gap;
  463.   py = gap;
  464.   maxheight = 0;
  465.   for (i = 0; i < len; i++) {
  466.     if (glist[i] != 0) {  /* If not already processed... */
  467.  
  468.       /* Try to find a character from the glist that will fit on the
  469.          remaining space on the current row. */
  470.  
  471.       int foundWidthFit = 0;
  472.       int c;
  473.  
  474.       getMetric(fontinfo, glist[i], &tgi);
  475.       width = tgi.width;
  476.       height = tgi.height;
  477.       if (height > 0 && width > 0) {
  478.         for (j = i; j < len;) {
  479.           if (height > 0 && width > 0) {
  480.             if (px + width + gap < texw) {
  481.               foundWidthFit = 1;
  482.           if (j != i) {
  483.         i--;  /* Step back so i loop increment leaves us at same character. */
  484.           }
  485.               break;
  486.             }
  487.       }
  488.           j++;
  489.           getMetric(fontinfo, glist[j], &tgi);
  490.           width = tgi.width;
  491.           height = tgi.height;
  492.         }
  493.  
  494.         /* If a fit was found, use that character; otherwise, advance a line
  495.            in  the texture. */
  496.         if (foundWidthFit) {
  497.           if (height > maxheight) {
  498.             maxheight = height;
  499.           }
  500.           c = j;
  501.         } else {
  502.           getMetric(fontinfo, glist[i], &tgi);
  503.           width = tgi.width;
  504.           height = tgi.height;
  505.  
  506.           py += maxheight + gap;
  507.           px = gap;
  508.           maxheight = height;
  509.           if (py + height + gap >= texh) {
  510.             printf("Overflowed texture space.\n");
  511.             exit(1);
  512.           }
  513.           c = i;
  514.         }
  515.  
  516.         /* Place the glyph in the texture image. */
  517.         placeGlyph(fontinfo, glist[c], texarea, texw, px, py);
  518.  
  519.         /* Assign glyph's texture coordinate. */
  520.         tgi.x = px;
  521.         tgi.y = py;
  522.  
  523.     /* Advance by glyph width, remaining in the current line. */
  524.         px += width + gap;
  525.       } else {
  526.     /* No texture image; assign invalid bogus texture coordinates. */
  527.         tgi.x = -1;
  528.         tgi.y = -1;
  529.       }
  530.       glist[c] = 0;     /* Mark processed; don't process again. */
  531.       /*CONSTANTCONDITION*/
  532.       assert(sizeof(tgi) == 12);  /* Ensure external file format size. */
  533.       fwrite(&tgi, sizeof(tgi), 1, file);
  534.     }
  535.   }
  536.  
  537.   switch (format) {
  538.   case TXF_FORMAT_BYTE:
  539.     fwrite(texarea, texw * texh, 1, file);
  540.     break;
  541.   case TXF_FORMAT_BITMAP:
  542.     stride = (texw + 7) >> 3;
  543.     texbitmap = (unsigned char *) calloc(stride * texh, 1);
  544.     for (i = 0; i < texh; i++) {
  545.       for (j = 0; j < texw; j++) {
  546.         if (texarea[i * texw + j] >= 128) {
  547.           texbitmap[i * stride + (j >> 3)] |= 1 << (j & 7);
  548.         }
  549.       }
  550.     }
  551.     fwrite(texbitmap, stride * texh, 1, file);
  552.     free(texbitmap);
  553.     break;
  554.   default:
  555.     printf("Unknown texture font format.\n");
  556.     exit(1);
  557.   }
  558.   free(texarea);
  559.   fclose(file);
  560. }
  561.